package com.showapi.app.service

import com.alibaba.fastjson.JSON
import com.showapi.app.dynamic.BaseHandler
import com.showapi.app.util.CommonUtil
import groovy.io.FileType
import org.apache.commons.lang3.StringUtils
import org.springframework.context.annotation.Configuration
import org.springframework.core.io.ClassPathResource
import com.showapi.app.util.ConstantsOfShowapi

@Configuration
class DynamicService extends BaseService {
	
	static def dirPath="springboot${File.separator}handler_of_dynamic".toString()
	static def handler_path
	static def allClassCacheMap=[:]//存放所有类 ,key是文件的path,value是[md5:"",clazz:aClass]
	static def handlerClassCacheMap=[:]//只存放handler类,key是文件的path,value是[md5:"",clazz:aClass]
	static def instanceCacheMap=[:]//实例缓存,key是className,value是instance

	static ShowapiClassLoader showapiClassLoader
	def getRetObj=CommonUtil.getRetObj
	def run_backgroud={className,methodName="doAction",para=[:]->
		if(ConstantsOfShowapi.IS_DEV){
			reloadClass()//开发环境，则每次请求都reloadClass，改过的文件才会编译
		}

        boolean can_run=true
		for(int i=0;i<10;i++){
			if(ConstantsOfShowapi.dynamicServiceLockMap.get(className)){
				can_run=false
				Thread.sleep(500)
			}else{
				can_run=true
				break
			}
		}
		if(!can_run){
			return  getRetObj(-1,'系统繁忙，请稍候再试')
		}
		def item=handlerClassCacheMap.get(className)  //注意是从handlerClassCacheMap中取
		if(!item){
			return  getRetObj(-1,'找不到处理Class')
		}
		def handler=instanceCacheMap.get(className)  //注意是从handlerClassCacheMap中取
		if(!handler){
			return  getRetObj(-1,'找不到处理handler')
		}

		if(methodName==null)methodName="doAction"
		if(para==null)para=[:]

		println "run in "+className+"    "+methodName
		def ret
		try{
			ret=handler."${methodName}"(para)
		}catch(Exception e){
			e.printStackTrace()
			ret= getRetObj(-1,'方法调用出错')
		}
//		println "dynamicService result is:"+ret.toString()
		return ret;
		
		
	}


	//通过类名获取一个实例
	public BaseHandler getInstance(className){
		return instanceCacheMap.get(className)
	}
	
	
	//检查父级或工具类改变情况。如果改变，则全体重新编译
	public void checkParentModify(){
		boolean needAllReload=false
		allClassCacheMap.find{className,item->
			if(item.isParentOrUtil){//只检查父级或工具类
				def f=new File(item.filePath)
				def src=f.getText("utf-8")
				def now_md5=src.md5()
				if(item.md5!=now_md5){
					needAllReload=true //标记为需要全部重新加载
					return 1
				}
			}
		}
		
		println "needAllReload  is: "+needAllReload
		if(needAllReload){
			allClassCacheMap.clear()
		}
		
	}

	public void init(){
		if(showapiClassLoader)return
		ClassLoader parent = this.class.getClassLoader();
		if(ConstantsOfShowapi.IS_DEV){//需要找到classpath
			def filepath=new File("").getAbsolutePath()
			handler_path="${filepath}/src/main/resources/static/${dirPath}/"
			println "in   DEVELOPMENT env,handler_path is :"+handler_path
		}else{
			ClassPathResource resource = new ClassPathResource("application.properties" );

			def filepath=new File(resource.getFile().getParentFile(),"static").getAbsolutePath() //向上一级
			handler_path="${filepath}/${dirPath}/".toString()
			println "in   PRODUCTION env,filepath is :"+filepath
			println "in   PRODUCTION env,handler_path is :"+handler_path
		}

		showapiClassLoader=new ShowapiClassLoader(parent);
	}


	//填充allClassCacheMap
//	http://localhost:802/myapp?s=dynamic&m=reloadClass
	public void  reloadClass(classNameList=null){
		try{
			init()
			checkParentModify()
			def temp_map=[:]
			if(classNameList){	//将其转换为map
				classNameList= JSON.parseArray(classNameList)
				classNameList.each{
					temp_map.put(it,1)
				}
			}
			new File(handler_path.toString()).eachFileRecurse(FileType.FILES){ File file->
				def path=file.getAbsolutePath()
                if(!path.endsWith(".groovy"))return

				int ind=path.indexOf(dirPath)
				int end=path.lastIndexOf(".groovy")
				if(end==-1)return
				def className=path.substring(ind+dirPath.size()+1,end)  //+1是把斜线也算进去了
				className=StringUtils.replace(className, "\\",".")
				className=StringUtils.replace(className, "/",".")
				if(temp_map&&!temp_map.get(className)){  //不是指定的className,返回
					return
				}
				ConstantsOfShowapi.dynamicServiceLockMap.put(className,1)
				println ".............go into  load class "+className
				try{
					showapiClassLoader.loadClass(className)  //里面会递归加载import，extends及自身三样\
				}finally{
					ConstantsOfShowapi.dynamicServiceLockMap.remove(className)
					println "::::::::::::::leave  load class "+className
				}
			}
		}catch(Exception e){
			e.printStackTrace()
		}
	}
	
	
	//加载父类
	public static loadParentClass={src->
		int ind=src.indexOf("{")  //读到这个标记认为引用头结束
		src=src.substring(0,ind)
		def  pack_name
		src.eachLine{
			def line=it.trim()
			if(!line)return
			if(line.startsWith("//"))return
			if(line.contains("*"))return
			if(!pack_name){
				line.find(/(?is)\s?package\s+([\w\.]+)/){mat,pa->
					pack_name=pa
				}
				if(pack_name)return  //找到就返回
			}
			
			boolean is_import=false
			line.find(/(?is).*?import\s+([\w\.]+)/){mat,className->
//				println "loadCommonClass    "+className
				is_import=true
				showapiClassLoader.loadClass(className)  //加载import
			}
			if(!is_import){ //加载extend
				line.find(/(?is)\s?class\s+[\w]+\s+extends\s+([\w\.]+)/){mat,className->
					if(!className.contains(".")){//不是全名
						className=pack_name+"."+className
					}
					showapiClassLoader.loadClass(className)
					def item=handlerClassCacheMap.get(className)
					if(item){
						handlerClassCacheMap.remove(className) //extend的类不出现在此列表里
						item.isParentOrUtil=true  //是父级或工具类
					}
					
				}
			}
		}
	}
	
	
	public static class  ShowapiClassLoader extends  ClassLoader{
		public ShowapiClassLoader(parentLoader){
			super(parentLoader)
		}
		protected Class<?> findClass(String className)
				throws ClassNotFoundException {
			def pack_path=className.replaceAll("\\.","/")
			def filePath=handler_path+pack_path+".groovy"
//			println filePath
			def f=new File(filePath)
			if(!f.exists())return null
			def item=allClassCacheMap.get(className)

			def src
			def modify_time=f.lastModified()
			if(!item){//如果没加载过
				src=f.getText("utf-8")
				DynamicService.loadParentClass(src)  //先加载父类
			}else{
				if(item.modify_time==modify_time){	//时间没改过，直接返回
					println "same modify_time ,find in cache: "+item.clazz
					return item.clazz
				}
				//运行到这里，说明文件肯定改过
				src=f.getText("utf-8")

			}
			def now_md5=src.md5()
            Class clazz
			if(item?.md5==now_md5) {	//时间改变过，但内容没变
				println "different modify_time,but same content ,find in cache: "+item.clazz
				item.modify_time=modify_time
				return item.clazz

			}else{//没加载过，或内容不同
				try{
					GroovyClassLoader onceClassLoader=new GroovyClassLoader(this )
					println("parse file1 "+className)
					clazz=onceClassLoader.parseClass(f)
					item=[md5:now_md5,clazz:clazz,filePath:filePath,modify_time:modify_time]
					if(BaseHandler.class.isAssignableFrom(clazz)){ //放入handler缓存
						handlerClassCacheMap.put(className,item)
						def ins=clazz.newInstance()
						ins.ctx=ConstantsOfShowapi.ctx
						instanceCacheMap.put(className,ins)

					}else{
						item.isParentOrUtil=true  //是父级或工具类
					}
					allClassCacheMap.put(className,item)

				}catch(Exception e){
					e.printStackTrace()
					log.error("reloadClass  err")

				}

			}
			return clazz
		}
		
	}

}
